#pragma once

#include "ffdefine.h"

void ffinit(int loglvl);

class ffmpeg {

public:
    ffmpeg();
    ~ffmpeg();
public:
    int run(int argc, char** argv);

    void remove_avoptions(AVDictionary** a, AVDictionary* b);
    void assert_avoptions(AVDictionary* m);

    int guess_input_channel_layout(InputStream* ist);

    enum AVPixelFormat choose_pixel_fmt(AVStream* st, AVCodecContext* avctx, AVCodec* codec, enum AVPixelFormat target);
    void choose_sample_fmt(AVStream* st, AVCodec* codec);

    int configure_filtergraph(FilterGraph* fg);
    int configure_output_filter(FilterGraph* fg, OutputFilter* ofilter, AVFilterInOut* out);
    void check_filter_outputs(void);
    int ist_in_filtergraph(FilterGraph* fg, InputStream* ist);
    int filtergraph_is_simple(FilterGraph* fg);
    int init_simple_filtergraph(InputStream* ist, OutputStream* ost);
    int init_complex_filtergraph(FilterGraph* fg);

    void sub2video_update(InputStream* ist, AVSubtitle* sub);

    int ifilter_parameters_from_frame(InputFilter* ifilter, const AVFrame* frame);

    int ffmpeg_parse_options(int argc, char** argv);

    int videotoolbox_init(AVCodecContext* s);
    int qsv_init(AVCodecContext* s);
    int cuvid_init(AVCodecContext* s);

    HWDevice* hw_device_get_by_name(const char* name);
    int hw_device_init_from_string(const char* arg, HWDevice** dev);
    void hw_device_free_all(void);

    int hw_device_setup_for_decode(InputStream* ist);
    int hw_device_setup_for_encode(OutputStream* ost);

    int hwaccel_decode_init(AVCodecContext* avctx);

    int transcode_init();
    int transcode_step(void);
    int transcode_from_filter(FilterGraph* graph, InputStream** best_ist);
    int transcode(void);

    void ffmpeg_cleanup(int ret);

    void do_video_stats(OutputStream* ost, int frame_size);
    BenchmarkTimeStamps get_benchmark_time_stamps(void);
    int64_t getmaxrss(void);
    int ifilter_has_all_input_formats(FilterGraph* fg);

private:
    double parse_number_or_die(const char *context, const char *numstr, int type, double min, double max);
    int64_t parse_time_or_die(const char *context, const char *timestr, int is_duration);
public:
    virtual void exit_program(int ret);
public:
    int run_as_daemon;
    int nb_frames_dup;
    unsigned dup_warning;
    int nb_frames_drop;
    int64_t decode_error_stat[2];

    int want_sdp;

    BenchmarkTimeStamps current_time;
    

    uint8_t* subtitle_out;

    InputStream** input_streams;
    int nb_input_streams;
    InputFile**   input_files;
    int nb_input_files;

    OutputStream** output_streams;
    int nb_output_streams;
    OutputFile**   output_files;
    int nb_output_files;

    int nb_hw_devices;
    HWDevice** hw_devices;

    AVBufferRef* hw_device_ctx;
    HWDevice* filter_hw_device;

    char* sdp_filename;
    
    int video_sync_method;

    int input_stream_potentially_available;

    volatile int ffmpeg_exited;
    int main_return_code;

    volatile int received_nb_signals;
    atomic_int transcode_init_done;

    AVIOContext* progress_avio;

    AVIOInterruptCB int_cb;
    OptionsContext  context;

#if HAVE_TERMIOS_H
    struct termios oldtty;
    int restore_tty;
#endif
};
